<style scoped>
.infinite-wrapper .loading-icon {
	margin: 1rem auto;
}
.infinite-wrapper .loading-scroll {
	margin: .4rem auto;
}

.hr {
	display: flex;
	align-items: center;
	justify-content: center;
	margin-top: .4rem;
	margin-bottom: .4rem;
	color: rgba(0, 0, 0, .45);
	font-size: .24rem;

	&::before {
		content: "";
		width: 1rem;
		height: 1px;
		margin-right: .2rem;
		background-color: rgba(0, 0, 0, .1);
	}

	&::after {
		content: "";
		width: 1rem;
		height: 1px;
		margin-left: .2rem;
		background-color: rgba(0, 0, 0, .1);
	}
}

.no-data-wrapper {
	margin: 1rem auto;
	color: rgba(0, 0, 0, .45);
	font-size: .24rem;
	text-align: center;
}
</style>

<template>
	<div class="infinite-wrapper">
		<template v-if="loading">
			<slot name="loading">
				<loading class="loading-icon" :r="16" :strokeWidth="2" :time="2" />
			</slot>
		</template>

		<template v-else>
			<template v-if="noData">
				<slot name="no-data">
					<div class="no-data-wrapper">{{noDataTips}}</div>
				</slot>
			</template>

			<template v-else>
				<slot name="card" v-bind:response="response" v-bind:dataList="dataList"></slot>

				<div id="io" ref="io" v-if="isNeedScrollLoad"></div>

				<template v-if="loadingScroll">
					<slot name="loading-scroll">
						<!-- <div class="loading-scroll">
							<hoc-svg class="loading-icon" :width="30" :height="30" fill="#00B2F8" name="loading"></hoc-svg>
						</div> -->

						<loading class="loading-scroll" :r="10" :strokeWidth="2" :time="2" />
					</slot>
				</template>

				<template v-if="! isOnce && isTips && loadedScroll">
					<slot name="complete">
						<div class="hr">{{completeTips}}</div>
					</slot>
				</template>
			</template>
		</template>
	</div>
</template>

<script>
	export default {
		name: 'infinite',

		props: {
			url: {
				type: String,
				required: true
			},
			params: {
				type: Object,
				default: () => ({})
			},
			pageSize: {
				type: Number,
				default: 10
			},
			pageIndexName: {
				type: String,
				default: 'pageIndex'
			},
			pageSizeName: {
				type: String,
				default: 'pageSize'
			},
			listName: {
				type: String,
				default: 'dataList'
			},
			countName: {
				type: String,
				default: 'count'
			},
			isTips: {
				type: Boolean,
				default: true
			},

			// 是否只需要加载一次
			isOnce: {
				type: Boolean,
				default: false
			},

			// 处理返回的数据
			filterMethod: {
				type: Function
			},
			noDataTips: {
				type: String,
				default: '暂无数据'
			},
			completeTips: {
				type: String,
				default: '已经到底了'
			},

			strokeColor: {
				type: String,
				default: '#00B2F8'
			},

			isDisabledFirstLoad: {
				type: Boolean,
				default: false
			},
			isPullRefresh: {
				type: Boolean
			}
		},

		data() {
			return {
				loading: false,
				loaded: false,
				loadingScroll: false,
				loadedScroll: false,

				// 是否观察过
				isObserved: false,

				page: 1,
				pageTotal: 1,

				originDataListLength: 0,

				response: {},
				dataList: []
			}
		},

		computed: {
			noData() {
				return this.loaded && ! this.dataList.length
			},
			isNeedScrollLoad() {
				if (this.isOnce) {
					return false
				}

				return this.pageTotal > this.originDataListLength
			}
		},

		watch: {
			isPullRefresh(value) {
				if (value) {
					this.page = 1
					this.pageTotal = 1

					this.originDataListLength = 0

					this.setDataList(true).then(this.handleDataListLoaded).then(() => {
						this.$emit('loadComplete')
					})
				}
			},
			params(value) {
				if (this.loading) {
					this.$source.cancel('取消上次请求')

					// 标记取消了
					this.$isCancel = true
				}

				// 重置
    			this.$source = this.$CancelToken.source()

				this.page = 1
				this.pageTotal = 1

				this.loaded = false
				this.loading = true
				this.loadedScroll = false
				this.isObserved = false

				this.originDataListLength = 0

				this.dataList = []

				this.setDataList().then(this.handleDataListLoaded)
			}
		},

		created() {
			// 取消请求
			this.$CancelToken = axiosOrigin.CancelToken
    		this.$source = this.$CancelToken.source()

			if (! this.isDisabledFirstLoad) {
				this.$timer = setTimeout(() => {
					this.loading = true
				})

				this.setDataList().then(this.handleDataListLoaded)
			}
		},

		methods: {
			async setDataList(isReplace) {
				const params = {
					[this.pageIndexName]: this.page,
					[this.pageSizeName]: this.pageSize,
					... this.params
				}

				const {success, data} = await axios.get(this.url, {params, cancelToken: this.$source.token})

				if (! success) {
					return
				}

				this.response = data

				this.page++
				this.pageTotal = data[this.countName]

				// 记录原始数据 length
				this.originDataListLength += data[this.listName].length

				// 数据处理
				if (this.filterMethod) {
					data[this.listName] = this.filterMethod(data[this.listName])
				}

				if (isReplace) {
					this.dataList = data[this.listName]
				} else {
					this.dataList = [... this.dataList, ... data[this.listName]]
				}

				// 加载完成后，取消观察
				if (! this.isOnce) {
					if (data[this.countName] === this.originDataListLength) {
						// 加载完成
						this.loadedScroll = true

						this.$refs.io && this.$observer.unobserve(this.$refs.io)
					}
				}
			},
			handleDataListLoaded() {
				// 是取消请求就拒绝处理
				if (this.$isCancel) {
					this.$isCancel = null

					return
				}

				clearTimeout(this.$timer)

				this.loading = false
				this.loaded = true

				// 抛出事件
				this.$emit('complete')

				// 是否需要加载更多
				if (this.isNeedScrollLoad && ! this.isObserved) {
					this.isObserved = true

					setTimeout(() => {
						this.observe()
					})
				}
			},
			observe() {
				this.$observer = new IntersectionObserver((entries) => {
					if (entries[0].intersectionRatio) {
						this.loadingScroll = true

						this.setDataList().then(() => {
							this.loadingScroll = false
						})
					}
				})

				this.$observer.observe(this.$refs.io)
			}
		}
	}
</script>